﻿using System;

namespace zijian666.AnyExtensions;

/// <summary>
/// 时间类型的扩展方法
/// </summary>
public static class DateTimeExtensions
{
    /// <summary>
    /// hh:mm:ss
    /// </summary>
    public static string ToTimeString(this TimeSpan timespan) => timespan.ToString(@"hh\:mm\:ss");

    /// <summary>
    /// yyyy-MM-dd HH:mm:ss
    /// </summary>
    public static string ToDateTimeString(this DateTime datetime) => datetime.ToString("yyyy-MM-dd HH:mm:ss");

    /// <summary>
    /// yyyy-MM-dd
    /// </summary>
    public static string ToDateString(this DateTime datetime) => datetime.ToString("yyyy-MM-dd");

    /// <summary>
    /// HH:mm:ss
    /// </summary>
    public static string ToTimeString(this DateTime datetime) => datetime.ToString("HH:mm:ss");

    /// <summary>
    /// 获取此实例时间所在的那一天的最后一秒
    /// </summary>
    public static DateTime LastSecondsOfDay(this DateTime datetime) => datetime.Date.AddDays(1).AddSeconds(-1);

    /// <summary>
    /// 获取此实例时间所在的那个月的第一天
    /// </summary>
    public static DateTime FirstDayOfMonth(this DateTime datetime) => datetime.AddDays(-datetime.Day + 1);

    /// <summary>
    /// 获取此实例时间所在的那个月的最后一天
    /// </summary>
    public static DateTime LastDayOfMonth(this DateTime datetime) => datetime.FirstDayOfMonth().AddMonths(1).AddDays(-1);

    /// <summary>
    /// 获取此实例时间所在的那一年的第一天
    /// </summary>
    public static DateTime FirstDayOfYear(this DateTime datetime) => datetime.AddDays(-datetime.DayOfYear + 1);

    /// <summary>
    /// 获取此实例时间所在的那一年的最后一天
    /// </summary>
    public static DateTime LastDayOfYear(this DateTime datetime) => datetime.AddDays(-datetime.DayOfYear).AddYears(1);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期日
    /// </summary>
    /// <param name="firstDayOfWeek">设置每周第一日是星期几</param>
    public static DateTime SundayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Sunday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期一
    /// </summary>
    public static DateTime MondayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Monday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期二
    /// </summary>
    public static DateTime TuesdayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Tuesday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期三
    /// </summary>
    public static DateTime WednesdayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Wednesday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期四
    /// </summary>
    public static DateTime ThursdayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Thursday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期五
    /// </summary>
    public static DateTime FridayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Friday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期六
    /// </summary>
    public static DateTime SaturdayOfWeek(this DateTime datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Saturday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那个月的第一天
    /// </summary>
    private static DateTime SetDayOfWeek(DateTime datetime, DayOfWeek week, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
    {
        if (datetime.DayOfWeek == week)
        {
            return datetime;
        }
        var flag = 7 - (int)firstDayOfWeek;
        var day = (((int)week + flag) % 7) - (((int)datetime.DayOfWeek + flag) % 7);
        return datetime.AddDays(day);
    }

    /// <summary>
    /// yyyy-MM-dd HH:mm:ss
    /// </summary>
    public static string ToDateTimeString(this DateTimeOffset datetime) => datetime.ToString("yyyy-MM-dd HH:mm:ss");

    /// <summary>
    /// yyyy-MM-dd
    /// </summary>
    public static string ToDateString(this DateTimeOffset datetime) => datetime.ToString("yyyy-MM-dd");

    /// <summary>
    /// HH:mm:ss
    /// </summary>
    public static string ToTimeString(this DateTimeOffset datetime) => datetime.ToString("HH:mm:ss");

    /// <summary>
    /// 获取此实例时间所在的那一天的最后一秒
    /// </summary>
    public static DateTimeOffset LastSecondsOfDay(this DateTimeOffset datetime) => datetime.Date.AddDays(1).AddSeconds(-1);

    /// <summary>
    /// 获取此实例时间所在的那个月的第一天
    /// </summary>
    public static DateTimeOffset FirstDayOfMonth(this DateTimeOffset datetime) => datetime.AddDays(-datetime.Day + 1);

    /// <summary>
    /// 获取此实例时间所在的那个月的最后一天
    /// </summary>
    public static DateTimeOffset LastDayOfMonth(this DateTimeOffset datetime) => datetime.FirstDayOfMonth().AddMonths(1).AddDays(-1);

    /// <summary>
    /// 获取此实例时间所在的那一年的第一天
    /// </summary>
    public static DateTimeOffset FirstDayOfYear(this DateTimeOffset datetime) => datetime.AddDays(-datetime.DayOfYear + 1);

    /// <summary>
    /// 获取此实例时间所在的那一年的最后一天
    /// </summary>
    public static DateTimeOffset LastDayOfYear(this DateTimeOffset datetime) => datetime.AddDays(-datetime.DayOfYear).AddYears(1);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期日
    /// </summary>
    public static DateTimeOffset SundayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Sunday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期一
    /// </summary>
    public static DateTimeOffset MondayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Monday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期二
    /// </summary>
    public static DateTimeOffset TuesdayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Tuesday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期三
    /// </summary>
    public static DateTimeOffset WednesdayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Wednesday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期四
    /// </summary>
    public static DateTimeOffset ThursdayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Thursday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期五
    /// </summary>
    public static DateTimeOffset FridayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Friday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那一周的星期六
    /// </summary>
    public static DateTimeOffset SaturdayOfWeek(this DateTimeOffset datetime, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
        => SetDayOfWeek(datetime, DayOfWeek.Saturday, firstDayOfWeek);

    /// <summary>
    /// 获取此实例时间所在的那个月的第一天
    /// </summary>
    private static DateTimeOffset SetDayOfWeek(DateTimeOffset datetime, DayOfWeek week, DayOfWeek firstDayOfWeek = DayOfWeek.Monday)
    {
        if (datetime.DayOfWeek == week)
        {
            return datetime;
        }
        var flag = 7 - (int)firstDayOfWeek;
        var day = (((int)week + flag) % 7) - (((int)datetime.DayOfWeek + flag) % 7);
        return datetime.AddDays(day);
    }
}